package main

import (
	"fmt"
	"sync"
	"time"
)

// Go 单例
// go 语言中不存在 static 的概念。
type MySQLDriver struct {
	username string
}

var mySQLDriver *MySQLDriver

// 并发访问-大概率会创建多个 MySQLDriver 实例。
func GetDriver() *MySQLDriver {
	if mySQLDriver == nil {
		fmt.Println("create instance ...")
		mySQLDriver = &MySQLDriver{}
	}
	return mySQLDriver
}

var lock sync.Mutex

// 创建实例只会调用一次，但后续的每次调用都需要加锁从而带来了不必要的开销。
func GetDriver1() *MySQLDriver {
	lock.Lock()
	defer lock.Unlock()
	if mySQLDriver == nil {
		fmt.Println("create instance ...")
		mySQLDriver = &MySQLDriver{}
	}
	fmt.Println("has created instance")
	return mySQLDriver
}

// 这是一个典型的双重检查的单例，
// 这里做了两次检查便可以避免后续其他线程再次访问锁。
func GetDriver2() *MySQLDriver {
	if mySQLDriver == nil {
		lock.Lock()
		defer lock.Unlock()
		if mySQLDriver == nil {
			fmt.Println("create instance ...")
			mySQLDriver = &MySQLDriver{}
		}
	}
	fmt.Println("has created instance")
	return mySQLDriver
}

var once sync.Once

func GetDriver3() *MySQLDriver {
	once.Do(func() {
		if mySQLDriver == nil {
			fmt.Println("create instance ...")
			mySQLDriver = &MySQLDriver{}
		}
	})
	fmt.Println("has created instance")
	return mySQLDriver
}

func main() {
	for i := 0; i < 10; i++ {
		go GetDriver2()
	}
	time.Sleep(2000 * time.Millisecond)
}
